﻿using PrinterMonitorSvc.Common;
using PrinterMonitorSvc.Core;
using PrinterMonitorSvc.Core.Attribute;
using PrinterMonitorSvc.Ext;
using PrinterMonitorSvc.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace PrinterMonitorSvc.Controller
{
    /// <summary>
    /// PDF控制器
    /// </summary>
    [RequestMapping("printpdf")]
    public class PdfController : ControllerBase
    {
        public PdfController(HttpListenerRequest request, HttpListenerResponse response) : base(request, response)
        {
        }

        /// <summary>
        /// 打印（GET）
        /// </summary>
        [GetMapping]
        public string PrintByGet()
        {
            try
            {
                string msg = null;
                logModel.TotalTime = WatchHelper.Watch(() =>
                {
                    //业务类型
                    var svcType = QueryString["serviceType"];
                    //文件地址
                    var pdfurl = QueryString["pdfurl"];
                    //打印数量
                    var printCount = QueryString["printCount"] == null ? 1 : int.Parse(QueryString["printCount"]);

                    logModel.PrintCopyNum = printCount;

                    var printerName = SetPrinter(svcType);
                    logModel.Content = $"业务类型：{svcType}\r\nURL：{pdfurl}\r\n打印机：{printerName}\r\n打印份数：{printCount}";
                    string path = null;
                    logModel.DownloadFileTime = WatchHelper.Watch(() =>
                    {
                        path = FileHelper.GenTempFile(pdfurl, Guid.NewGuid() + ".pdf");
                    }, out logModel.DownloadStartTime, out logModel.DownloadEndTime, out Exception downloadEx);
                    if (null != downloadEx)
                    {
                        throw downloadEx;
                    }
                    if (printCount > 1)
                    {
                        //合并打印文件,并发送至打印队列
                        logModel.MergeFileTime = WatchHelper.Watch(() =>
                        {
                            MergeFile(new List<PrintTaskModel> { new PrintTaskModel(logModel.RequestId, printerName, path, printCount) });
                        }, out logModel.MergeStartTime, out logModel.MergeEndTime, out Exception mergeEx);
                        if (null != mergeEx)
                        {
                            throw mergeEx;
                        }
                    }
                    else
                    {
                        MonitorContext.PrintQueue.Enqueue(new List<PrintTaskModel> { new PrintTaskModel(logModel.RequestId, printerName, path, printCount) });
                    }

                    msg = string.Format("PrinterName：{0},ServiceType:{1},URL:{2},Print instructions have been sent。", printerName, svcType, pdfurl);


                }, out logModel.RequestStartTime, out logModel.RequestEndTime, out Exception requestEx);
                if (null != requestEx)
                {
                    throw requestEx;
                }
                return msg;

            }
            finally
            {
                LogPrintInfo();
            }
        }

        /// <summary>
        /// 打印（POST）
        /// </summary>
        [PostMapping]
        public string PrintByPost()
        {
            try
            {
                StringBuilder sb = new StringBuilder();
                logModel.TotalTime = WatchHelper.Watch(() =>
                {
                    logModel.Content = BodyString;
                    if (!string.IsNullOrEmpty(logModel.Content))
                    {
                        var printModelList = JsonHelper.ConvertObj<List<PrintModel>>(logModel.Content);
                        printModelList.ForEach(s =>
                        {
                            if (s.PrintCount <= 0)
                            {
                                s.PrintCount = 1;
                            }
                        });
                        logModel.TotalFileNum = printModelList.Count;
                        logModel.PrintCopyNum = printModelList.Sum(s => s.PrintCount);
                        List<PrintTaskModel> printTaskModels = new List<PrintTaskModel>();
                        var groupList = printModelList.GroupBy(s => s.FileUrl).ToList();
                        logModel.DownloadFileNum = groupList.Count;

                        logModel.DownloadFileTime = WatchHelper.Watch(() =>
                        {
                            groupList.AsParallel().WithDegreeOfParallelism(10).ForAll(g =>
                            {
                                var path = FileHelper.GenTempFile(g.Key, Guid.NewGuid() + ".pdf");
                                foreach (var item in g)
                                {
                                    var printerName = SetPrinter(item.ServiceType);
                                    item.PrintTask = new PrintTaskModel(logModel.RequestId, printerName, path, item.PrintCount);
                                }
                            });
                        }, out logModel.DownloadStartTime, out logModel.DownloadEndTime, out Exception downloadEx);

                        if (null != downloadEx)
                        {
                            throw downloadEx;
                        }

                        foreach (var printModel in printModelList)
                        {
                            printTaskModels.Add(printModel.PrintTask);
                            sb.AppendLine(string.Format("PrinterName：{0},ServiceType:{1},PrintCount:{2},URL:{3},Print instructions have been sent。", printModel.PrintTask.PrinterName, printModel.ServiceType, printModel.PrintCount, printModel.FileUrl));
                        }

                        //合并打印文件,并发送至打印队列
                        logModel.MergeFileTime = WatchHelper.Watch(() =>
                        {
                            MergeFile(printTaskModels);
                        }, out logModel.MergeStartTime, out logModel.MergeEndTime, out Exception mergeEx);
                        if (null != mergeEx)
                        {
                            throw mergeEx;
                        }

                    }
                    else
                    {
                        throw new Exception("The POST request parameter cannot be null");
                    }
                }, out logModel.RequestStartTime, out logModel.RequestEndTime, out Exception requestEx);
                if (null != requestEx)
                {
                    throw requestEx;
                }
                return sb.ToString();
            }
            finally
            {
                LogPrintInfo();
            }
        }


        /// <summary>
        /// 选择并返回打印机名称
        /// </summary>
        /// <param name="svcType">业务类型</param>
        /// <returns></returns>
        private string SetPrinter(string svcType)
        {
            var defaultPrinterName = PrinterHelper.DefaultPrinter;

            //根据业务类型设置打印机
            var printerName = GetPrinterNameByServiceType(svcType);
            if (string.IsNullOrEmpty(printerName))
            {
                printerName = defaultPrinterName;
            }
            return printerName;
        }

        /// <summary>
        /// 根据业务类型获取打印机名称
        /// </summary>
        /// <param name="serviceType"></param>
        /// <returns></returns>
        private string GetPrinterNameByServiceType(string serviceType)
        {
            if (serviceType.IsEmpty())
            {
                throw new Exception("The serviceType cannot be null");
            }
            var config = ConfigController.GetServiceConfig();
            var service = config?.FirstOrDefault(s => s.Key == serviceType);
            return service?.Value;
        }

        /// <summary>
        /// 合并打印任务并发送到打印队列
        /// </summary>
        /// <param name="tasks"></param>
        /// <returns></returns>
        private void MergeFile(List<PrintTaskModel> tasks)
        {
            tasks.GroupBy(s => s.PrinterName).AsParallel().WithDegreeOfParallelism(10).ForAll(taskGroup =>
            {
                try
                {
                    List<string> pdfPathList = new List<string>();

                    foreach (var task in taskGroup)
                    {
                        for (int j = 0; j < task.PrintCount; j++)
                        {
                            pdfPathList.Add(task.Pdfurl);
                        }
                    }

                    var partitionList = pdfPathList.partition(100);

                    foreach (var partition in partitionList)
                    {
                        var path = FileHelper.GenTempFile(Guid.NewGuid() + ".pdf");
                        PdfHelper.MergePdfFiles(partition, path);
                        List<PrintTaskModel> taskList = new List<PrintTaskModel>();
                        taskList.Add(new PrintTaskModel(taskGroup.First().Id, taskGroup.Key, path));
                        MonitorContext.PrintQueue.Enqueue(taskList);
                    }
                }
                catch (Exception ex)
                {
                    MonitorContext.LogError($"合并打印任务并发送到打印队列异常,{ex}");
                }

            });


        }

        /// <summary>
        /// 记录打印信息
        /// </summary>
        private void LogPrintInfo()
        {
            string msg = $"请求ID：{logModel.RequestId}  --  总耗时：{logModel.TotalTime}MS  --  文件下载耗时：{logModel.DownloadFileTime}MS  --  文件合并耗时：{logModel.MergeFileTime}MS  --  文件总数量：{logModel.TotalFileNum}  --  下载文件数量：{logModel.DownloadFileNum}  --  打印文件数量：{logModel.PrintCopyNum}" +
                                        $"\r\n请求开始：{logModel.RequestStartTime}  --  请求结束：{logModel.RequestEndTime}" +
                                        $"\r\n文件下载开始：{logModel.DownloadStartTime}  --  文件下载结束：{logModel.DownloadEndTime}" +
                                        $"\r\n文件合并开始：{logModel.MergeStartTime}  --  文件合并结束：{logModel.MergeEndTime}" +
                                        $"\r\n请求参数：\r\n{logModel.Content}";
            MonitorContext.LogInfo(msg);
        }
    }
}
